package org.sl.flvplayer.helper {
	import flash.display.BitmapData;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.AsyncErrorEvent;
	import flash.events.Event;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.URLRequest;

	/**
	 * Generates a previewimage from a videoframe in the video or loads an external image.
	 * 
	 * @package org.sl.flvplayer.helper
	 * @author Dan
	 * @version 
	 * @see org.sl.flvplayer.flyweights.FWPreviewImage
	 */ 
	public class PreviewGenerator extends Sprite {
		private var nc : NetConnection;
		private var ns : NetStream;
		private var videoHolder : Sprite = new Sprite();
		private var video : Video;
		private var videoPos : Number;
		private var metadata : Object;
		private var callbackInstance : *;
		private var videoSrc : String;
		private var server : String = null;

		/**
		 * Checks for a time so seek in the video or a path to load the previewimage and depending what parameter is available establishes a new NetConnection or loads the external image.
		 * 
		 * @param p_callbackInstance instance which initialized the <code>PreviewGenerator</code>
		 * @param p_src	path to the image source
		 * @param p_time time in seconds of the video
		 */
		public function renderImage(p_callbackInstance : *, p_src : String, p_time : Number = -1) : void {
			videoPos = p_time;
			videoSrc = p_src;
			callbackInstance = p_callbackInstance;
			// checks if p_time is a valid seekposition
			// note the number has to be within range of the actual videoduration or otherwise an error can occur
			if (p_time >= 0) {
				nc = new NetConnection();
				nc.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
				nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
				nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
				// server can be set here for rtmp streaming
				nc.connect(server);							
			} else {
				var imageLoader : Loader = new Loader();
				imageLoader.load(new URLRequest(p_src));
				imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderComplete);
			}			
		}

		/**
		 * Sends the drawn bitmapData object to the setPreviewImage method of the callbackInstance.  
		 * 
		 * @param p_event
		 */
		private function onLoaderComplete(p_event : Event) : void {
			// 
			var imageLoader : Loader = Loader(p_event.target.loader); 
			var imageBitmapData : BitmapData = new BitmapData(imageLoader.width, imageLoader.height, true, 0xffffff);	
			imageBitmapData.draw(imageLoader.content, null, null, null, null, true);
			callbackInstance.setPreviewImage(imageBitmapData);
		}

		/**
		 * Handles netStream status events.
		 * 
		 * @param p_event
		 */
		private function onNetStatus(p_event : NetStatusEvent) : void {	
			switch (p_event.info.code) {
				case "NetStream.Seek.Notify":
					this.addEventListener(Event.ENTER_FRAME, addPreview);
					// FLVPlayerApplication.SOS_TRACER.alert("Succes!! ");
					break;
				case "NetConnection.Connect.Success":	
					// play stream if connection successful
					connectStream();				
					break;		
				case "NetStream.Seek.Failed":
					// FLVPlayerApplication.SOS_TRACER.fatal("Fail ");
					// ns.seek(videoPos);
					break;			
				case "NetStream.Seek.InvalidTime":
					// FLVPlayerApplication.SOS_TRACER.debug("InvalidTime " + videoPos);			
					ns.seek(videoPos);
					break;
			}
		}

		/**
		 * Establishes a new NetStream, adds the necessary listeners and ensures that the metadata is available.
		 */
		private function connectStream() : void {
			ns = new NetStream(nc);
			ns.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
			ns.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
			ns.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			// other event handlers assigned 
			// to the netstream client property
			var metadataListener : Object = new Object();
			metadataListener.onMetaData = onMetaDataHandler;
			//netstream object
			ns.client = metadataListener;				
			// attach netstream to the video object
			video = new Video();
			video.attachNetStream(ns);		
			// ns.bufferTime = 6;	
			ns.play(videoSrc);
		}

		/**
		 * When the metadata is available the onMetaDataHandler function seeks to the desired position in the video from which a previewimage is taken. 
		 * 
		 * @param p_metaInfoObj metadata
		 */
		private function onMetaDataHandler(metaInfoObj : Object) : void {			
			metadata = metaInfoObj;
			ns.pause();
			ns.seek(videoPos);
		}

		/**
		 * Handles async errors.
		 * 
		 * @param p_event
		 */
		private function asyncErrorHandler(p_event : AsyncErrorEvent) : void {
			//		
		}

		/**
		 * Handles security errors.
		 * 
		 * @param p_event
		 */
		private function securityErrorHandler(p_event : SecurityErrorEvent) : void {
			//
		}

		/**
		 * Draws the videoframe as a bitmap which prior to that was attached to a sprite allowing width and height of the metadata to be applied. 
		 * 
		 * @param p_event
		 */
		private function addPreview(p_event : Event) : void {				
			this.removeEventListener(Event.ENTER_FRAME, addPreview);									
			var videoBitmapData : BitmapData = new BitmapData(metadata.width, metadata.height, false, 0xffffff);		
			video.width = metadata.width;
			video.height = metadata.height;
			// metadata width and height aren`t applied on the video as long as the displayed object (here the videoframe) isn`t in the displaylist 
			// by attaching the video to a Sprite the video is succesfully listed in the displaylist and thus the metadata are applied
			videoHolder.addChild(video);			
			videoBitmapData.draw(videoHolder);			
			callbackInstance.setPreviewImage(videoBitmapData);
			ns.close();
		}
	}
}